/* Copyright 2014 Jan Wolter */

/* This is for canfield variants with a finite number of passes through the
 * deck allowed.
 *
 * THIS DOESN'T WORK RIGHT: Game 1531 is winnable, but it can't win it.
 */
#include "stack.h"
#include "tableau.h"
#include "found.h"
#include "stock.h"
#include "waste.h"
#include "solve.h"
#include "game.h"

char *gamename= "Canfield-Finite";
char *gameopt= "OF";
Stack *gathered= NULL;
Stack *temp= NULL;

int reservei;

int nplayed= 0;
int nwon= 0;
int nabandoned= 0;
int cardsleft= 0;
int maxoff;
int baserank;

int autofill= 1;

void setopt(char c)
{
    switch (c)
    {
	case 'F': autofill= 0; break;  /* Turn off autofills */
    }
}

/* Given the pip value of a card, what is it's foundation rank, where the
 * basecard's baserank is 0, the next card is 1, and so on.
 */
int basepip(int n)
{
    return (n - baserank + npip)%npip;
}


/* This is the main program for setting up the current game */
void exec(int gather, int arif, int over, int cut)
{

    /* Could set things like ndeck, nsuit here */

    /* Shuffle the deck */
    Stack *shuf= mixcards(arif, over, cut);

    /* The first card will be the base card for our foundations */
    Card *basecard= popStack(shuf);
    baserank= basecard->n;

    /* Create the foundations */
    superfound= sf_constbase;
    for (int s= 0; s < nsuit; s++)
    {
	makeFound(baserank, s, +1, 13);
	/* If the suit of this foundation matches our basecard put it on */
	if (s == basecard->s)
	    pushStack(stacks+foundation[nfound-1].id, basecard);
    }

    /* Create the reserve - part of the tableau really */
    makeTableau(13, FLIP_ALL, shuf, S_NONE, 0, S_NONE, 0, 0, F_NONE, BREAK_ANYWHERE, 0);
    reservei= ntableau-1;

    /* Create the tableau */
    for (int i= 1; i <= 4; i++)
	makeTableau(1, FLIP_NONE, shuf,
	    S_DIFFCOLOR, -1, S_DIFFCOLOR, -1, baserank, F_ANYTHING, 0, reservei+1);

    /* Create the stock/waste - really a bouquet with chunksize 3 */
    makeWaste();
    makeStock(DT_WASTE, 1, 3, shuf);

    freeStack(shuf);

    /* Print initial state */
    if (verbose > 1) printState(stdout, stacks);

    maxoff= 0;
    int win= solve(gather,0);

    if (verbose > 0) printf("maxoff=%d\n",maxoff);
    nplayed++;
    cardsleft+= maxoff;
    if (win == 1)
	nwon++;
    else if (win == 2)
	nabandoned++;

    cleanFound();
    cleanTableau();
    cleanWaste();
    cleanStock();
    cleanStacks();
    cleanVisited();
}

/* Gather up the cards into the global 'gathered' stack. This has to be
 * non-destructive, since we may not be done solving when it is called.
 */
void gatherAll()
{
    if (gathered == NULL)
	gathered= allocStack(ncard);
    else
	gathered->n= 0;

    gatherFound(gathered);
    gatherTableau(gathered);
    gatherWaste(gathered,0);
    /* Don't need to gather stock, because it's always empty when we are done */
    if (gathered->n != ncard)
    {
	printf("Gather Failed - found %d cards\n",gathered->n);
	exit(1);
    }
}

/* How good is a move?
 *
 * Returns 0 if the move is completely safe or manditory. Such moves will not
 * be marked as choice points for backtracking.
 *
 * For other moves, positive values are returned with lower values for better
 * moves.
 *
 * Negative values mean the move should not be done at all.
 *
 * So for Canfield:
 *
 *   0 = Actual safe moves to foundation 
 *       and auto fills of tableau spaces from reserve.
 *   1 = moves from reserve to tableau, except last card in reserve.
 *       moves that empty a tableau pile.
 *   2-10 = moves from waste that build toward connecting tableau piles or
 *          lifting reserve pile. Rating is 1 + distance from moved card toward
 *          card we are building toward.
 *   2-10 = moves to foundation that build toward lifting top card of reserve
 *          or bottom card of a tableau pile.
 *   20 = moves from mid-waste to safe foundation spaces
 *   21 = moves to unsafe foundation spaces
 *   22 = tableau to tableau moves that open no empty spaces, but expose a card
 *        that could be moved to the foundation.
 *   23 = deal card from stock to waste.
 *   24 = moves from waste to tableau that build toward nothing
 *   -1 = tableau to tableau moves that open no empty spaces and don't expose
 *        any card that could be moved to the foundation.
 */
int rateMove(Stack *src, int index1, int len, Stack *dst)
{
    Card *mc= src->c[index1];

    /* Dealing cards from stock is never a good move */
    if (src->type == STOCK)
	return 23;

    if (dst->type == FOUND)
    {
	FoundStack *f= foundation + dst->tid;
	/* Moving Aces and Twos to the foundation is safe, but
	 * they aren't necessarily aces and two in Canfield. */
	int safe= (mc->n == f->baserank || mc->n == (f->baserank%npip) + 1);
	if (!safe)
	{
	    /* Find the two foundation stacks with different colors */
	    /* Here we assume suitcolor[] contains alternate ones and zeros */
	    int f1= (dst->tid + 1)%nsuit;
	    int f2= (dst->tid + 3)%nsuit;
	    /* We want to make sure our pile doesn't get more than two cards
	     * ahead of the piles of the opposite suit.
	     */
	    int safe= (dst->n - 1 <= stacks[f1].n &&
	               dst->n - 1 <= stacks[f2].n);
	}
	int bestrc;
	if (safe)
	    return 0;
	else
	    bestrc= 21;
	/* Now we are left with unsafe foundation moves. These are high priority
	 * if they build toward lifting the top reserve pile or an only card on
	 * a tableau, otherwise they are low priority.
	 */
	int mn= basepip(mc->n);
	for (int k= 0; k < ntableau; k++)
	{
	    /* Skip empty stacks */
	    if (stacks[tableau[k].id].n == 0 ||
	        (k != reservei && stacks[tableau[k].id].n != 1)) continue;

	    Card *gc;
	    if (k == reservei)
		/* For Reserve, want to build to top card */
		gc= topStack(stacks + tableau[k].id);
	    else
		/* For Tableau, want to build to bottom card */
		gc= stacks[tableau[k].id].c[0];
	    /* This is a possible target to build to if it is the same
	     * suit and of lower rank */
	    int gn= basepip(gc->n);
	    if (gn > mn && gc->s == mc->s)
	    {
		int rc= gn - mn + 1;
		if (rc < bestrc) bestrc= rc;
	    }
	}
	return bestrc;
    }
    else
    {
	/* Moving to tableau */
	if (src->type == TABLEAU)
	{
	    if (src->tid == reservei)
	    {
		/* Moving from reserve */

		/* Moving a reserve card into an empty space is not only safe,
		 * it's manditory.
		 */
		if (dst->n == 0 && autofill) return 0;
		/* Generally moving cards out of the reserve is a very high
		 * priority, except that there is no particular rush to remove
		 * the last one.
		 */
		if (src->n > 1) return 1;

		/* otherwise drop through */
	    }
	    else
	    {
		/* Moving from another tableau pile */

		/* If this move empties a tableau pile, even if the reserve is
		 * already empty, then it is a fine idea.
		 */
		if (index1 == 0) return 1;

		/* Other tableau to tableau moves must necessarily be breaking
		 * up an already built sequence.  These moves are useful only
		 * if they expose a card that can be moved to a foundation
		 * pile. Don't even consider them otherwise.
		 */
		Card *expcard= src->c[index1 - 1];
		for (int k= 0; k < nfound; k++)
		    if (found_mayInsert(stacks+foundation[k].id,expcard,1,src))
			    return 22;
		return -1;
	    }
	}
	/* Here we are moving either a waste card or the last reserve card
	 * to the tableau. This is more useful if it builds toward joining
	 * two tableau piles, or lifting the top reserve card.
	 */
	int bestrc= 24; /* return code for building toward nothing */

	/* All cards fit either in red-odd sequences or black-odd sequences.
	 * Where does this one fit? */
	int blackodd= (mc->n + suitcolor[mc->s]) % 2;

	/* Loop through the tableaus and reserve, looking for things we might
	 * be building toward
	 */
	int mn= basepip(mc->n);
	for (int k= 0; k < ntableau; k++)
	{
	    /* Skip empty stacks */
	    if (stacks[tableau[k].id].n == 0) continue;

	    Card *gc;
	    if (k == reservei)
		/* For Reserve, want to build to top card */
		gc= topStack(stacks + tableau[k].id);
	    else
		/* For Tableau, want to build to bottom card */
		gc= stacks[tableau[k].id].c[0];
	    /* This is a possible target to build to if it is also
	     * red-odd/black-odd and if it has a lower pip value than
	     * the moved card.  */
	    int gn= basepip(gc->n);
	    if (gn < mn && blackodd == (gc->n + suitcolor[gc->s]) % 2)
	    {
		int rc= mn - gn + 1;
		if (rc < bestrc) bestrc= rc;
	    }
	}
	return bestrc;
    }
}

void printState(FILE *f, Stack *stks)
{
    printFound(f, stks);
    printTableau(f, stks);
    printStock(f, stks);
    printWaste(f, stks);
}

int victory()
{
    int off= nOff();
    if (off > maxoff) maxoff= off;
    return (off == ncard);
}

/* Return a quality rating for the current solution. Bigger is better. Usually
 * it's just cards off. This is used to decide which solution to report in
 * cases where we never win.
 */
int quality()
{
    return nOff();
}

void summary()
{
    printf("Played:    %d games\n",nplayed);
    printf("Won:       %d games (%.2f%)\n",nwon,(float)nwon*100/nplayed);
    printf("Abandoned: %d games (%.2f%)\n",nabandoned,(float)nabandoned*100/nplayed);
    printf("Cards Off: %.2f\n",(float)cardsleft/nplayed);
}

/* Return a string that uniquely describes the current state. */
char *currstate()
{
    /* ncards for all the cards on things, 4 for tableaus, 1 for reserve,
     * 4 for list terminators.
     */
    char buf[ncard+22], *p= buf;

    /* Numbers of cards in each foundation pile */
    /* We add one to each count because we don't want any zeros */
    for (int k= 0; k < nfound; k++)
	*(p++)= stacks[foundation[k].id].n + 1;

    /* Count of cards on the reserve */
    *(p++)= stacks[tableau[reservei].id].n + 1;

    /* List of cards on each tableau pile - probably we should normalize
     * the order of tableau piles, sorting them by the first card on them,
     * but that is unlikely to be a bit deal in Canfield. Also the flippedness
     * of cards may need to matter, but again, that doesn't matter in Canfield.
     */
    for (int k= 0; k < ntableau; k++)
    {
	if (k == reservei) continue;
	Stack *s= stacks + tableau[k].id;
	for (int i= 0; i < s->n; i++)
	    *(p++)= (s->c[i] - deck) + 1;
	*(p++)= ncard + 1; /* The card list terminator */
    }

    /* Current Pass number and Number of cards in stack */
    *(p++)= passcnt;

    *(p++)= stacks[stock[0].id].n + 1;
    
    /* We don't need to list cards in stock/waste. We know which ones are out
     * and their order never changes. */

    *p= '\0';

    return strdup(buf);
}
